package main

import (
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/joho/godotenv"
)

type ExecRequest struct {
	ID   string `json:"id"`
	Code string `json:"code"`
}

type ExecResponse struct {
	ID     string `json:"id"`
	Output string `json:"output"`
	Error  string `json:"error"`
}

var currentPath string

func main() {
	err := godotenv.Load()
	if err != nil {
		fmt.Println("Miss loading .env file", err)
	}
	wd, err := os.Getwd()
	if err != nil {
		fmt.Println("Failed to get wd:", err)
		return
	}
	currentPath = wd

	router := gin.Default()
	router.POST("/exec", execHandler)
	router.Run("0.0.0.0:" + os.Getenv("SERVER_PORT"))
}

func execHandler(c *gin.Context) {
	var request ExecRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request payload"})
		return
	}

	// 0. create tmp file
	timestamp := time.Now().Format("20060102150405")
	fileDirName := fmt.Sprintf("%s_%s", request.ID, timestamp)
	fileDir := filepath.Join(currentPath, "files", fileDirName)
	filePath := fmt.Sprintf("%s/execer.go", fileDir)
	volumeDir := filepath.Join(os.Getenv("HOST_FILE_DIR_PATH"), fileDirName)

	err := os.MkdirAll(fileDir, 0755)
	if err != nil {
		fmt.Println("Failed to create directories:", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create directories"})
		return
	}
	file, err := os.Create(filePath)
	if err != nil {
		fmt.Println("Failed to create file:", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create file"})
		return
	}
	defer func() {
		file.Close()
		os.Remove(filePath)
		os.RemoveAll(fileDir)
	}()

	_, err = file.WriteString(request.Code)
	if err != nil {
		fmt.Println("Failed to write file:", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write file"})
		return
	}

	// 1. use docker to run golang code
	containerName := fmt.Sprintf("execer_%s_%s", request.ID, timestamp)
	command := fmt.Sprintf("docker run --rm -t --name=%s -v %s:/app golang:1.21.3-alpine go run /app/execer.go", containerName, volumeDir)
	fmt.Println(command)
	cmd := exec.Command("sh", "-c", command)

	output, err := cmd.CombinedOutput()
	if err != nil {
		response := ExecResponse{
			ID:     request.ID,
			Output: "",
			Error:  extractErrorStackTrace(string(output)),
		}
		c.JSON(http.StatusOK, response)
		return
	}

	response := ExecResponse{
		ID:     request.ID,
		Output: string(output),
		Error:  "",
	}
	c.JSON(http.StatusOK, response)
}

func extractErrorStackTrace(output string) string {
	lines := strings.Split(output, "\n")
	if len(lines) > 0 {
		parts := strings.SplitN(lines[0], ":", 3)
		if len(parts) == 3 {
			return parts[1] + ":" + parts[2]
		}
	}
	return output
}
